#include "globaldef.h"
#include <util/delay.h>
#include <avr/io.h>
#include <stdbool.h>
#include "lcd.h"
#include "lcdprivate.h"

/**
  * Initializes the LCD so that it is ready display values.
  *
  * The initialization process must configure the appropriate port
  * by sending initialization commands to the LCD.  The port used in
  * the initialization here will be used by the other functions in
  * this library.
  *
  * @param port A character 'A', 'B', 'C' or 'D' designating the port
  *               to which the LCD is connected.
  */
void lcd_init(char port){
	lcd_port = port;

	if( lcd_port =='A')
		DDRA = 0xFF;		//set to output
	else if( lcd_port =='B')
		DDRB = 0xFF;		//set to output
	else if( lcd_port =='C')
		DDRC = 0xFF;		//set to output
	else 
		DDRD = 0xFF;		//set to output
	lcd_cmd_write(0x33);    //set 8 bit mode
	_delay_ms(2);
	lcd_cmd_write(0x32);	//set 8 bit mode
	lcd_cmd_write(0x28);	//set data length to 4
	lcd_cmd_write(0x0F);	//turn on display, cursor, blink
	lcd_clear();
	lcd_home();
}

/**
  * Send the cursor to the home position on the LCD.
  */
void lcd_home(void){
	lcd_cmd_write(0x02);	//send cursor to home position
}

/**
  * Clear the LCD panel.
  */
void lcd_clear(void){
	lcd_cmd_write(0x01);	//clear display
	lcd_cmd_write(0x06);	//auto increment and disale shift mode. 
	//lcd_cmd_write(0x02);	
}

/**
  * Move the cursor to the specified position on the LCD panel.
  *
  * @param x Horizontal position of the cursor.
  * @param y Vertical position of the cursor.
  */
void lcd_goto_xy(uint8_t x,uint8_t y){
	lcd_home();
	if(y==1)
		for(uint8_t i=0; i < 40; i++)
			lcd_cmd_write(LCD_CURSOR_RIGHT);	//Move the cursor to the right 40 times to get to the next line

	for(uint8_t i=0; i<x; i++)
		lcd_cmd_write(LCD_CURSOR_RIGHT);		//Move the cursor to the right the specified amount of times
}

/**
  * Display a character on the LCD panel at the cursor's current position.
  *
  * @param symbol Character to be displayed.
  */
void lcd_print_char(char symbol){
	lcd_data_write(symbol);
}

/**
  * Display a sequence of characters on the LCD panel beginning at
  * the cursor's current position.
  *
  * @param string A pointer to a null-terminated sequence of characters.
  */
void lcd_print_string(char string[]){
	for(uint8_t i=0; string[i] != '\0'; i++)
		lcd_print_char(string[i]); 				//output the ith character
}

/**
  * Display a hexidecimal value on the LCD panel beginning at the
  * cursor's current position.
  *
  * @param hex The value to be displayed.
  */
void lcd_print_hex(uint8_t hex){
	lcd_print_string("0x");						//print out the lead "0x"
	uint8_t upper = hex >> 4;
	upper &= 0x0f;								//mask upper half
	hex &= 0x0f;								//mask upper half
	upper += '0';								//add ascii character offset
	hex += '0';									//add ascii character offset
	if(upper >= '9' +1)
		upper +=7;								//if it's not a digit add another 7
	if(hex >= '9' +1)
		hex +=7;								//if it's not a digit add another 7
	lcd_data_write(upper);
	lcd_data_write(hex);
}

/**
  * Display an unsiged 8-bit integer value on the LCD panel beginning at the
  * cursor's current position.
  *
  * @param no The value to be displayed.
  */
void lcd_print_uint8(uint8_t no){
	uint8_t temp =0;
	if(no >99){
		temp=no/100;				// take off the last two digits
		temp += '0';				// add the ascii character offset
		lcd_data_write(temp);		// write the first digit to the LCD
		temp -= '0';				// subtract the character offset
		no = no-(temp*100);			// subtract temp from no to get rid of first digit
	}
	if(no >9){
		temp = no/10;				// take off the last digit
		temp += '0';				// add the ascii character offset
		lcd_data_write(temp);		// write the second digit to the LCD
		temp -= '0';				// subtract the character offset
		no = no-(temp*10);			// subtract temp from no to get rid of second digit
	}else if(temp !=0){
		lcd_print_char('0');		// if the middle chracter of the original number was zero, output a zero
	}
	temp=no;
	temp += '0';					// add the ascii character offset	
	lcd_data_write(temp);			// write the last digit
}

/**
  * Display a signed 8-bit integer value on the LCD panel beginning at the
  * cursor's current position.
  *
  * IMPLEMENTATION OF THIS METHOD IS WORTH 5 POINTS.  You are not required
  * to implement it; however, the best grade you may receive on this assignment
  * is 95% if this method is not implemented.
  *
  * @param no The value to be displayed.
  */
void lcd_print_int8(int8_t no){
	if(no<0){
		lcd_print_char('-');		// print out the negative sign
		lcd_print_uint8(~(no)+1);	// perform two's compliment
	}else
		lcd_print_uint8(no);		// if not negative just output number
}

/**
  * Display an unsiged 16-bit integer value on the LCD panel beginning at the
  * cursor's current position.
  *
  * IMPLEMENTATION OF THIS METHOD IS OPTIONAL.
  *
  * @param no The value to be displayed.
  */
void lcd_print_uint16(uint16_t no){
}

/**
  * Display a signed 16-bit integer value on the LCD panel beginning at the
  * cursor's current position.
  *
  * IMPLEMENTATION OF THIS METHOD IS OPTIONAL.
  *
  * @param no The value to be displayed.
  */
void lcd_print_int16(int16_t no){
}

/**
  * Display a floating point value on the LCD panel beginning at the
  * cursor's current position.
  *
  * IMPLEMENTATION OF THIS METHOD IS OPTIONAL.
  *
  * @param no The value to be displayed.
  */
void lcd_print_float(float no){
}

/**
  * Send one byte of data to the LCD panel.
  *
  * @param data The data value to be sent to the LCD panel.
  */
void lcd_data_write(uint8_t data){
	lcd_write(data, true);
}

/**
  * Send a one byte command to the LCD panel.
  *
  * @param cmd The command to be sent to the LCD panel.
  */
void lcd_cmd_write(uint8_t cmd){
	lcd_write(cmd, false);
}

/**
  * Write one byte (cmd or data) to the LCD panel.
  *
  * @param value The command/data to be sent to the LCD panel.
  * @param is_data non-zero if writing data, zero if writing a command.
  */
void lcd_write(uint8_t value, _Bool is_data){
	
	lcd_write_nibble(value, is_data);		//write the upper nibble
	lcd_write_nibble(value*16,is_data);		//write the lower nibble
	lcd_sbi(LCD_CTRL_RS);
	lcd_sbi(LCD_CTRL_WR);
}

/**
  * Write one nibble to the LCD panel.  The nibble to be written should
  * be in the lower half of the byte passed.
  *
  * @param value The nibble (in the lower four bits) to be sent to the LCD panel.
  * @param is_data non-zero if writing data, zero if writing a command.
  */
void lcd_write_nibble(uint8_t data, _Bool is_data){
	data &= 0xF0;			//clear lower half
	if(is_data==true)
		data |= (1 << LCD_CTRL_RS);
	write_lcd_port(data);	//write data to the screen
	lcd_sbi(LCD_CTRL_E); 	//latch
	_delay_ms(2);
	lcd_cbi(LCD_CTRL_E);	//unlatch
	_delay_ms(2);
}

/**
  * Sends data to the port to which the LCD is attached.
  * If lcd_port contains a corrupt value, the default port (PORTC) will be used.
  *
  * @param data The value to be written to the port.
  */
void write_lcd_port(uint8_t data){
	if(lcd_port == 'A')
		PORTA = data;			//output data to portA
	else if (lcd_port == 'B')
		PORTB = data;			//output data to portB
	else if ( lcd_port == 'C')
		PORTC = data;			//output data to portC
	else
		PORTD = data;			//output data to portD
}

/**
  * Sends data to DDR for the to which the LCD is attached.
  * If lcd_port contains a corrupt value, the default port (PORTC) will be used.
  *
  * @param value The value to be written to the port.
  */
void write_lcd_ddr(uint8_t data){
	if(lcd_port == 'A')
		DDRA = data;			//output data to DDRA
	else if (lcd_port == 'B')
		DDRB = data;			//output data to DDRB
	else if ( lcd_port == 'C')
		DDRC = data;			//output data to DDRC
	else
		DDRD = data;			//output data to DDRD
}

/**
  * Sets a bit in the port to which the LCD is attached.
  * If lcd_port contains a corrupt value, the default port (PORTC) will be used.
  *
  * @param bit The bit (0-7) to be set in the port.
  */
void lcd_sbi(uint8_t bit){
	if(lcd_port == 'A')
		PORTA = PORTA | (1<<bit);	//set specified bit on portA
	else if (lcd_port == 'B')
		PORTB |= (1<<bit);			//set specified bit on portB
	else if ( lcd_port == 'D')
		PORTD |= (1<<bit);			//set specified bit on portC
	else
		PORTC |= (1<<bit);			//set specified bit on portD
}

/**
  * Clears a bit in the port to which the LCD is attached.
  * If lcd_port contains a corrupt value, the default port (PORTC) will be used.
  *
  * @param bit The bit (0-7) to be cleared in the port.
  */
void lcd_cbi(uint8_t bit){
	if(lcd_port == 'A')				
		PORTA = PORTA & ~(1<<bit);	//clear specified bit on portA
	else if (lcd_port == 'B')
		PORTB &= ~(1<<bit);			//clear specified bit on portB
	else if ( lcd_port == 'D')
		PORTD &= ~(1<<bit);			//clear specified bit on portC
	else
		PORTC &= ~(1<<bit);			//clear specified bit on portD
}


